<script setup>
import * as THREE from 'three'

import Stats from 'three/addons/libs/stats.module.js'

import { TrackballControls } from 'three/addons/controls/TrackballControls.js'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'
import { onMounted } from 'vue'
import { ref } from 'vue'
import axios from 'axios'
import { min_x, max_x, min_y, max_y, min_z, max_z, basic_x, basic_y, ni, nj, nk } from '../config'
import { parseWell } from '../utils'
import { useRoute } from 'vue-router'

// http://localhost:5173/vis?color=0xffff00&level=z0,z100&pro=poro
/// parse route query
// const route = useRoute()
// const routeQuery = route.query
// console.log('route query:', routeQuery)

const colorList = [0x00ffff, 0x00ff00, 0x0000ff]
const currColor = ref(1)
const levelGrids = {
  z0: '/all_z0_BO3.csv',
  z200: '/all_z200_BO3.csv',
  z400: '/all_z400_BO3.csv',
  z500: '/all_z500_BO3.csv',
  y200: '/all_y200_BO3.csv',
  x200: '/all_x200_BO3.csv',
  z0c: '/all_z0c_BO3.csv',
  z1c: '/all_z0c_BO3.csv',
  xyz200: '/all_xyz200_BO3.csv',
}
const levelGridsOptions = ['z0', 'z200', 'z400', 'z500','y200','x200','xyz200','z0c','z1c']
const colorListOptions = [0, 1, 2]
const currLevel = ref(['z0'])
const proTypes = {
  types: ['poro', 'porv'],
  poro: 6,
  porv: 7,
}
const selectProType = ref('poro')
const wellinfo = ref([{ name: '...', xcoord: '...', ycoord: '...' }])
const isLoading = ref(false)
const showStatus = ref(false)
const color = localStorage.getItem('color')
const level = localStorage.getItem('level')
const pro = localStorage.getItem('pro')
if (color) {
  currColor.value = parseInt(color)
}
if (level) {
  currLevel.value = level.split(',')
}
if (pro) {
  selectProType.value = pro
}

const changePro = () => {
  alert('change')
}

let container, stats
let camera, controls, scene, renderer
let pickingTexture, pickingScene
let highlightBox
let clock = new THREE.Clock();

const pickingData = []

const msg = ref('')
const msg_l = ref('')
const cameraInfo = ref([])

const pointer = new THREE.Vector2()
const offset = new THREE.Vector3(2, 2, 2)
const clearColor = new THREE.Color()
const dataview = ref(null)

async function init() {
  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000)
  camera.position.x = -2328 // 5322 // -2190
  camera.position.y = -5465 // -1000 // -6091
  camera.position.z = 4070 // 4645 // -3003

  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xffffff)

  scene.add(new THREE.AmbientLight(0xcccccc))

  const light = new THREE.DirectionalLight(0xffffff, 3)
  light.position.set(0, 0, 1000)
  scene.add(light)

  const defaultMaterial = new THREE.MeshPhongMaterial({
    color: 0xffffff,
    flatShading: true,
    vertexColors: true,
    shininess: 0,
  })

  // set up the picking texture to use a 32 bit integer so we can write and read integer ids from it
  pickingScene = new THREE.Scene()
  pickingTexture = new THREE.WebGLRenderTarget(1, 1, {
    type: THREE.IntType,
    format: THREE.RGBAIntegerFormat,
    internalFormat: 'RGBA32I',
  })
  const pickingMaterial = new THREE.ShaderMaterial({
    glslVersion: THREE.GLSL3,

    vertexShader: /* glsl */ `
      attribute int id;
      flat varying int vid;
      void main() {
        vid = id;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
    `,

    fragmentShader: /* glsl */ `
      layout(location = 0) out int out_id;
      flat varying int vid;
      void main() {
        out_id = vid;
      }
    `,
  })

  function applyId(geometry, id) {
    const position = geometry.attributes.position
    const array = new Int16Array(position.count)
    array.fill(id)

    const bufferAttribute = new THREE.Int16BufferAttribute(array, 1, false)
    bufferAttribute.gpuType = THREE.IntType
    geometry.setAttribute('id', bufferAttribute)
  }

  function applyVertexColors(geometry, color) {
    const position = geometry.attributes.position
    const colors = []

    for (let i = 0; i < position.count; i++) {
      colors.push(color.r, color.g, color.b)
    }
    geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
  }

  isLoading.value = true

  let res = await axios.get(levelGrids[currLevel.value[0]])
  let data = res.data.split('\n').map((line) => line.split(','))
  let gridsData = []

  // color norm
  let min_c = 1
  let max_c = -1

  for (let j = 0; j < data.length; j++) {
    const c = parseFloat(data[j][proTypes[selectProType.value]])
    min_c = min_c > c ? c : min_c
    max_c = max_c < c ? c : max_c
  }

  for (let dataItem of currLevel.value) {
    if (dataItem === levelGrids[currLevel.value[0]]) {
      gridsData = gridsData.concat(data)
      continue
    }
    const t_res = await axios.get(levelGrids[dataItem])
    console.log(t_res.data.length, dataItem)
    const t_data = t_res.data.split('\n').map((line) => line.split(','))
    gridsData = gridsData.concat(t_data)
  }

  data = null

  const c_norm = (c) => {
    return (c - min_c) / (max_c - min_c)
  }

  const geometries = []
  const matrix = new THREE.Matrix4()
  const quaternion = new THREE.Quaternion()
  const color = new THREE.Color()

  const renderGridLevel = async (grids) => {
    console.log(grids[0])
    for (let gid = 0; gid < grids.length; gid++) {
      const color_c = parseFloat(grids[gid][proTypes[selectProType.value]]) * 3
      const geometry = new THREE.BoxGeometry()
      const nodes = grids[gid].slice(0, 7).map(Number)
      const position = new THREE.Vector3()

      position.x = nodes[0] - min_x - (max_x - min_x) / 2
      position.y = nodes[1] - min_y - (max_y - min_y) / 2
      position.z = nodes[2] - min_z - (max_z - min_z) / 2

      const dx = parseFloat(nodes[3])
      const dy = parseFloat(nodes[4])
      const dz = parseInt(nodes[5])
      if (gid < 10) {
        console.log(dx, dy, dz)
      }

      const rotation = new THREE.Euler()
      rotation.x = 0 * 2 * Math.PI
      rotation.y = 0 * 2 * Math.PI
      rotation.z = 0 * 2 * Math.PI
      const scale = new THREE.Vector3(dx, dy, dz)
      quaternion.setFromEuler(rotation)
      matrix.compose(position, quaternion, scale)
      geometry.applyMatrix4(matrix)
      applyVertexColors(geometry, color.setHex(c_norm(color_c) * colorList[currColor.value]))
      applyId(geometry, gid)
      geometries.push(geometry)
      pickingData[gid] = {
        position: position,
        rotation: rotation,
        scale: scale,
        poro: color_c,
      }
    }
    console.log('finfished', geometries.length)
  }

  // const gridsData = data200.concat(data).concat(data400)

  renderGridLevel(gridsData)

  const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries)
  scene.add(new THREE.Mesh(mergedGeometry, defaultMaterial))
  pickingScene.add(new THREE.Mesh(mergedGeometry, pickingMaterial))

  /** add wells */
  const wells = await parseWell()
  console.log(wells.length)
  let geometrie_wells = []
  wellinfo.value = wells
  console.log(wells)
  for (let k = 0; k < wells.length; k++) {
    const geometry = new THREE.BoxGeometry(1, 1, 1000)
    const x = wells[k].xcoord
    const y = wells[k].ycoord
    const z = wells[k].depth

    const position = new THREE.Vector3()
    position.x = x - min_x - (max_x - min_x) / 2
    position.y = y - min_y - (max_y - min_y) / 2
    position.z = 500

    const scale = new THREE.Vector3(12, 12, 1)

    matrix.compose(position, quaternion, scale)
    geometry.applyMatrix4(matrix)
    geometrie_wells.push(geometry)
  }
  geometrie_wells = BufferGeometryUtils.mergeGeometries(geometrie_wells)
  // yellow wells material
  const redMaterial = new THREE.MeshPhongMaterial({
    color: 0xff0000,
  })
  scene.add(new THREE.Mesh(geometrie_wells, redMaterial))

  console.log('finfished')

  highlightBox = new THREE.Mesh(
    new THREE.BoxGeometry(),
    new THREE.MeshLambertMaterial({ color: 0xffff00 }),
  )

  const dir_x = new THREE.Vector3(1, 0, 0)
  const dir_y = new THREE.Vector3(0, 1, 0)
  const dir_z = new THREE.Vector3(0, 0, 1)
  const origin = new THREE.Vector3(0, 0, 0)
  scene.add(new THREE.ArrowHelper(dir_x, origin, 100, 0xff0000))
  scene.add(new THREE.ArrowHelper(dir_y, origin, 100, 0x00ff00))
  scene.add(new THREE.ArrowHelper(dir_z, origin, 100, 0x0000ff))

  // axes helpers
  scene.add(highlightBox)
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setAnimationLoop(animate)
  dataview.value.appendChild(renderer.domElement)

  controls = new TrackballControls(camera, renderer.domElement)
  controls.rotateSpeed = 1.0
  controls.zoomSpeed = 1.2
  controls.panSpeed = 0.8
  controls.noZoom = false
  controls.noPan = false
  controls.staticMoving = true
  controls.dynamicDampingFactor = 0.3

  stats = new Stats()
  showStatus.value && dataview.value.appendChild(stats.dom)

  renderer.domElement.addEventListener('pointermove', onPointerMove)

  isLoading.value = false
}

function onPointerMove(e) {
  pointer.x = e.clientX
  pointer.y = e.clientY
}

function animate() {
  render()
  const _cameraInfo =
    camera.position.toArray().join(',') + ',' + camera.rotation.toArray().join(',')
  cameraInfo.value = _cameraInfo.split(',')
  stats.update()
}

function pick() {
  // render the picking scene off-screen
  // set the view offset to represent just a single pixel under the mouse
  const dpr = window.devicePixelRatio
  camera.setViewOffset(
    renderer.domElement.width,
    renderer.domElement.height,
    Math.floor(pointer.x * dpr),
    Math.floor(pointer.y * dpr),
    1,
    1,
  )

  // render the scene
  renderer.setRenderTarget(pickingTexture)

  // clear the background to - 1 meaning no item was hit
  clearColor.setRGB(-1, -1, -1)
  renderer.setClearColor(clearColor)
  renderer.render(pickingScene, camera)

  // clear the view offset so rendering returns to normal
  camera.clearViewOffset()

  // create buffer for reading single pixel
  const pixelBuffer = new Int32Array(4)

  // read the pixel
  renderer.readRenderTargetPixelsAsync(pickingTexture, 0, 0, 1, 1, pixelBuffer).then(() => {
    const id = pixelBuffer[0]
    if (id !== -1 && id > 0) {
      // move our highlightBox so that it surrounds the picked object
      // console.log(id)
      const data = pickingData[id]
      highlightBox.position.copy(data.position)
      // highlightBox.rotation.copy(data.rotation)
      const scale = new THREE.Vector3()
      scale.x = 100
      scale.y = 100
      scale.z = 20
      highlightBox.scale.copy(scale).add(offset)
      highlightBox.visible = true

      msg_l.value = {
        x: (data.position.x + min_x + (max_x - min_x) / 2 + basic_x).toFixed(2),
        y: (data.position.y + min_y + (max_y - min_y) / 2 + basic_y).toFixed(2),
        z: (data.position.z + min_z + (max_z - min_z) / 2).toFixed(2),
        id: id,
        poro: data.poro.toFixed(2),
      }
    } else {
      highlightBox.visible = false
    }
  })
}

function render() {
  controls.update()
  pick()
  renderer.setRenderTarget(null)
  renderer.render(scene, camera)
}

function add(a, b) {
  return parseFloat(a) + parseFloat(b)
}

function saveTolocalStorageAndRender() {
  localStorage.setItem('color', currColor.value)
  localStorage.setItem('level', currLevel.value)
  localStorage.setItem('pro', selectProType.value)
  window.location.reload()
}

function changeShowStatus() {
  if (showStatus.value) {
    showStatus.value = false
  } else {
    showStatus.value = true
  }
}

onMounted(async () => {
  await init()
})
</script>

<template>
  <div ref="dataview" style="z-index: 10" class=""></div>

  <div
    v-if="isLoading"
    style="position: absolute; left: 50vw; top: 50vh"
    class="flex justify-center items-center"
  >
    <div class="flex gap-2 mr-2">
      <div class="w-2 h-2 rounded-full animate-pulse bg-blue-600"></div>
      <div class="w-2 h-2 rounded-full animate-pulse bg-blue-600"></div>
      <div class="w-2 h-2 rounded-full animate-pulse bg-blue-600"></div>
    </div>
    正在加载中...
  </div>

  <div
    style="position: absolute; left: 0px; top: 16px"
    class="font-mono font-bold w-full text-xl flex justify-center items-center z-20"
  >
    <div class="bg-white flex justify-around items-center w-full px-4">
      <!-- draw line -->
      <div class="bg-gray-200 h-[1px] flex-1"></div>
      <div class="bg-gray-200 w-2 h-2 rounded-full"></div>
      <div class="px-4 font-semibold">油藏模型三维可视化</div>
      <div class="bg-gray-200 w-2 h-2 rounded-full"></div>
      <div class="bg-gray-200 h-[1px] flex-1"></div>
    </div>
  </div>

  <!-- <div style="position: absolute; left: 0px; top: 0px;height: 100dvh;width: 100dvw;">
    <div style="height: calc(100% - 64px);" class="mx-[16px] my-[32px] border w-[calc(100vw - 32px)] rounded-lg
   border-blue-600">
    </div>
  </div> -->

  <div style="position: absolute; left: 50px; top: 48px" class="flex flex-col gap-4 w-[260px]">
    <div
      class="text-mono text-sm z-10 border px-4 py-2 border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"
    >
      <div class="flex w-auto justify-center pb-2 font-semibold">网格坐标和属性信息</div>
      <div class="flex">
        <div class="w-20 text-right">GRID_ID:</div>
        <div class="w-full text-right">{{ msg_l.id || '-' }}</div>
      </div>
      <div class="flex text-sm">
        <div class="w-20 text-right">XCOORD:</div>
        <div class="w-full text-right">{{ msg_l.x || '-' }}</div>
      </div>
      <div class="flex">
        <div class="w-20 text-right">YCOORD:</div>
        <div class="w-full text-right">{{ msg_l.y || '-' }}</div>
      </div>
      <div class="flex">
        <div class="w-20 text-right">DEPTH:</div>
        <div class="w-full text-right">{{ msg_l.z || '-' }}</div>
      </div>

      <div class="flex">
        <div class="w-20 text-right">{{ selectProType.toUpperCase() }}:</div>
        <div class="w-full text-right">{{ msg_l.poro || '-' }}</div>
      </div>

      <!-- <div>camera:{{ cameraInfo }}</div> -->
    </div>

    <!-- page left -->
    <div
      class="text-mono z-10 border px-4 py-2 border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-sm"
    >
      <div class="font-mono flex w-auto justify-center pb-2 font-semibold">
        管道坐标点信息(加偏移)
      </div>
      <div class="">
        <!-- {{ wellinfo }} -->
        <div class="flex justify-between gap-2 text-blue-800">
          <div>WELL_NMAE</div>
          <div>XCOORD</div>
          <div>YCOORD</div>
        </div>
        <div v-for="item in wellinfo" :key="item" class="flex justify-between gap-2">
          <div>{{ item['name'] }}</div>
          <div>{{ item['xcoord'] }}</div>
          <div>{{ item['ycoord'] }}</div>
        </div>
      </div>
    </div>
  </div>

  <!-- page right -->
  <div
    style="position: absolute; right: 50px; top: 48px; z-index: 20"
    class="flex flex-col gap-4 w-[260px]"
  >
    <div
      class="text-mono z-10 border px-4 py-2 border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-sm"
    >
      <div class="font-mono flex w-auto justify-center pb-2 font-semibold">空间范围</div>

      <!-- min_x, max_x, min_y, max_y, min_z, max_z, basic_x, basic_y, ni, nj, nk -->
      <div class="flex justify-between">
        <div class="">BASIC_X:</div>
        <div class="">{{ basic_x || '-' }}</div>
      </div>

      <div class="flex justify-between">
        <div class="">BASIC_Y:</div>
        <div class="">{{ basic_y || '-' }}</div>
      </div>

      <div class="flex justify-between">
        <div class="">X Level nums:</div>
        <div class="">{{ ni || '-' }}</div>
      </div>
      <div class="flex justify-between">
        <div class="">Y Level nums:</div>
        <div class="">{{ nj || '-' }}</div>
      </div>
      <div class="flex justify-between">
        <div class="">Z Level nums:</div>
        <div class="">{{ nk || '-' }}</div>
      </div>

      <div class="flex justify-between">
        <div class="">X_RANGE:</div>
        <div class="">{{ min_x || '-' }} ~ {{ max_x || '-' }}</div>
      </div>
      <div class="flex justify-between">
        <div class="">Y_RANGE:</div>
        <div class="">{{ min_y || '-' }} ~ {{ max_y || '-' }}</div>
      </div>
      <div class="flex justify-between">
        <div class="">Z_RANGE:</div>
        <div class="">{{ min_z || '-' }} ~ {{ max_z || '-' }}</div>
      </div>
    </div>

    <div
      style=""
      class="text-mono z-10 border px-4 py-2 border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-sm flex flex-col gap-2 w-[260px]"
    >
      <!-- 下拉框选择颜色 -->
      <div class="font-mono flex w-auto justify-center font-semibold">交互性操作</div>

      <div class="w-32">选择颜色：</div>

      <el-select v-model="currColor" size="small" placeholder="Select" style="width: 230px">
        <el-option
          v-for="item in colorListOptions"
          :key="item"
          :label="colorList[item]"
          :value="item"
        />
      </el-select>

      <el-select
        v-model="currLevel"
        size="small"
        multiple
        placeholder="Select"
        style="width: 230px"
      >
        <el-option v-for="item in levelGridsOptions" :key="item" :label="item" :value="item" />
      </el-select>

      <div class="w-32">选择属性：</div>
      <el-select v-model="selectProType" size="small" placeholder="Select" style="width: 230px">
        <el-option v-for="item in proTypes['types']" :key="item" :label="item" :value="item" />
      </el-select>

      <el-button type="primary" @click="saveTolocalStorageAndRender" size="small">刷新界面</el-button>
    </div>

    <div
      class="text-mono border px-4 py-2 border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 text-sm"
    >
      <div class="font-mono flex w-auto justify-center pb-2 font-semibold">相机信息</div>
      <div class="flex">
        <div class="w-20 text-right">x,y,z:</div>
        <div class="w-full text-right">
          {{ cameraInfo[0]?.split('.')[0] || '-' }},{{ cameraInfo[1]?.split('.')[0] || '-' }},{{
            cameraInfo[2]?.split('.')[0] || '-'
          }}
        </div>
      </div>
      <div class="flex">
        <div class="w-20 text-right">rotation:</div>
        <div class="w-full text-right">
          {{ cameraInfo[3]?.slice(0, 5) || '-' }},{{ cameraInfo[4]?.slice(0, 5) || '-' }},{{
            cameraInfo[5]?.slice(0, 5) || '-'
          }}
        </div>
      </div>
    </div>
  </div>

  <!-- page bottom -->
  <div
    style="position: absolute; left: 0px; bottom: 16px"
    class="font-mono font-bold w-full text-2xl flex justify-center items-center z-20"
  >
    <div class="bg-white flex justify-around items-center w-full px-4">
      <!-- draw line -->
      <div class="bg-gray-200 h-[1px] flex-1"></div>
      <!-- <div class="bg-blue-800 w-2 h-2 rounded-full"></div> -->
      <div class="px-4 flex text-sm rounded-full border py-1">
        <div>亿级以上网格油藏模型三维可视化</div>
        <div>｜</div>
        <div>团队：南屏晚钟</div>
        <div>｜</div>
        <div>邮箱：pzding@163.com</div>
      </div>
      <!-- <div class="bg-blue-800 w-2 h-2 rounded-full"></div> -->
      <div class="bg-gray-200 h-[1px] flex-1"></div>
    </div>
  </div>
</template>
